<?php

class Mahjong 
{
	const CT_ERROR = "-1";	//错误牌型

	const PAI_ZFB   =            0 ;   //中，发，白  
	const PAI_FENG  =            1 ;  //东西南北风  
	const PAI_WAN   =            2 ;  //万  
	const PAI_TIAO  =            3 ;  //条  
	const PAI_BING  =            4 ;  //饼  
	const PAI_HUA   =            5 ;  //花  
	  
	const QiPAI     =       true ;   //起牌  
	const DaiPAI    =       false;  //打牌  

	//  m_Type      m_Value  
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-//  
	//  0       |   中   1   发2  白                                               
	//          |  
	//  1       |   东 1 西2  南     北                                    
	//          |  
	//  2       |   一万  二万  ……  九万  
	//          |  
	//  3       |   一条  二条  ……  九条                    
	//          |  
	//  4       |   一饼  二饼  ……  九饼  
	//          |  
	//  5       |   春       夏       秋       东       竹       兰       梅       菊  
	//          |  
	//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-//  

	public $m_9LBD = false;             //是否听连宝灯牌型  
	public $m_13Y = false;              //是否听十三幺  
	public $m_4AK = false;              //是否是听四暗刻  
	public $m_MKNum = 0;            //明刻数  
	public $m_AKNum = 0;            //暗刻数  

	public $m_TempChiPAIVec = array();    //吃的可选组合  
	public $tempPeng_options = array();   //碰的可选组合  
	public $tempGang_options = array();   //杠的可选组合  

	protected static $instance = null;



/*
	碰碰胡:四刻+对子.
	清一色：全条子(万子.桐子)组成的胡牌.
	将一色:全部由2 5 8电数的牌组成的胡牌.
	风一色:有的地方叫乱风,全字,由东南西北中发白组成的
	胡牌,且只要凑足牌的数量,不需要刻.
	杠上花:扛出来的牌刚好胡了,就叫杠上开花.
	海底捞月:最后一张牌抓进成胡牌.或胡人家打出的最
	一张牌
	抢杠和:人家是明杠的第四张牌如果你要胡的话,就可以胡.
*/



	//检测是否三连队
	private function checkAABBCC($v){
		return $v[0] == $v[1] && $v[2] == $v[3] && $v[4] == $v[5] && 
		$v[0] == $v[2] - 1 && $v[2] == $v[4] - 1;
	}
	//检测是否六连队
	// private function checkAABBCCDDEEFF($v){
	// 	return $v[0] == $v[1] && $v[2] == $v[3] && $v[4] == $v[5] && $v[6] == $v[7] && $v[8] == $v[9] && $v[10] == $v[11] &&
	// 	$v[0] == $v[10] - 5;
	// }

	//检测是否三连高压
	// private function checkAAABBBCCC($v){
	// 	return $v[0]==$v[2]  && $v[3]==$v[5] && $v[6]==$v[8] &&
	// 	$v[0] == $v[6] - 2 ;
	// }

	//检测是否三连刻
	private function checkAAAABBBBCCCC($v){
		return $v[0]==$v[3] &&  $v[4]==$v[7] && $v[8]==$v[11] &&
		$v[0] == $v[4] - 1 && $v[4] == $v[8] - 1;
	}

	private function check5Pai($v){
		if($this->checkAA($v) && $this->check3Pai(array_slice($v, 2, 3) )){
			return true;
		}
		if($this->checkAAA(array_slice($v, 1, 3)) && $this->check3Pai(array($v[0],$v[3],$v[4]) ) ){
			return true;
		}
		if($this->checkAA(array_slice($v, 3, 2)) && $this->check3Pai($v)){
			return true;
		}
		return false;
	}

	private function check6Pai($v){
		if($this->check3Pai($v) && $this->check3Pai(array_slice($v, 3, 3))){
			return true;
		}
		if($this->checkAABBCC($v)){
			return true;
		}
		if($this->checkAAAA(array_slice($v, 1, 4)) && $this->checkABC(array($v[0],$v[1],$v[5])) ){
			return true;
		}
		if(floor($v[0]/10)!= 5 && $v[0]==$v[1]-1 && $v[1]==$v[2] && $v[2]==$v[3]-1 && $v[3]==$v[4] && $v[4]==$v[5]-1 ){
			return true;
		}

		return false;
	}

	public function analysisCard($cards){
		sort($cards);

		$analysis['ke'] = array();
		$analysis['pair'] = array();
		$analysis['joker'] = array();
		$analysis = array(
			'joker' => array(),
			'ke' => array(),
			'shunABC' => array(),
			'pair' => array(),
			'shunAB' => array(),
			'shunAC' => array(),
			'singleAB' => array(),
			'singleAC' => array(),
			'single' => array(),
			);

		$other = array();
		for ($i=0; $i < count($cards); $i++) { 
			if($cards[$i] < 100 ){
				$analysis['joker'][] = $cards[$i];
			} else {
				$other = array_slice($cards, $i);
				break;
			}
		}

		$other_count = count($other);

		for($i=0; $i<$other_count; $i++)  
		{  
			$sameCnt=1;  
			for($j=$i+1; $j<$other_count; $j++)  
			{  
				if($other[$j] != $other[$i]){
					break;  
				} 
				$sameCnt++;
				if($sameCnt == 3)              //刻子
				{
					$analysis['ke'][]=$other[$i];
					$analysis['ke'][]=$other[$i+1];
					$analysis['ke'][]=$other[$i+2];
					unset($other[$i]);
					unset($other[$i+1]);
					unset($other[$i+2]);
					break;
				}  
			} 
			$i += $sameCnt-1;  
		}  

		$other = array_values($other);
		$other_count = count($other);

		for($i=0; $i<$other_count-2; $i++)  
		{  
			if($other[$i+1]-$other[$i]==1 && $other[$i+2]-$other[$i+1]==1){
				$analysis['shunABC'][]=$other[$i];
				$analysis['shunABC'][]=$other[$i+1];
				$analysis['shunABC'][]=$other[$i+2];
				unset($other[$i]);
				unset($other[$i+1]);
				unset($other[$i+2]);
				$i += 2;
				continue;
			}else if($other[$i+1]-$other[$i]==1&&$other[$i+2]==$other[$i+1]&&isset($other[$i+3])&&$other[$i+3]-$other[$i+2]==1){
				$analysis['shunABC'][]=$other[$i];
				$analysis['shunABC'][]=$other[$i+1];
				$analysis['shunABC'][]=$other[$i+3];
				unset($other[$i]);
				unset($other[$i+1]);
				$other[$i+3] = $other[$i+2];
				unset($other[$i+2]);
				$i += 2;
				continue;
			}
		}  

		$other = array_values($other);
		$other_count = count($other);

		for($i=0; $i<$other_count; $i++)  
		{  
			$sameCnt=1;  
			for($j=$i+1; $j<$other_count; $j++)  
			{  
				if($other[$j] != $other[$i]){
					break;  
				} 
				$sameCnt++;
				if($sameCnt == 2)              //刻字
				{
					$analysis['pair'][]=$other[$i];
					$analysis['pair'][]=$other[$i+1];
					unset($other[$i]);
					unset($other[$i+1]);
					break;
				}  
			}  
			$i += $sameCnt-1;  
		}  

		$other = array_values($other);
		$other_count = count($other);

		for($i=0; $i<$other_count; $i++)  
		{  
			$tmpCnt=1;  
			for($j=$i+1; $j<$other_count; $j++)  
			{  
				if($other[$j] - $other[$i] != 1 || $other[$i]%10==1 || $other[$j]%10==9){
					break;  
				} 
				$tmpCnt++;
				if($tmpCnt == 2)              //顺子
				{
					$analysis['shunAB'][]=$other[$i];
					$analysis['shunAB'][]=$other[$i+1];
					unset($other[$i]);
					unset($other[$i+1]);
					break;
				} 
			}  
			$i += $tmpCnt-1;  
		}  

		$other = array_values($other);
		$other_count = count($other);

		for($i=0; $i<$other_count; $i++)  
		{  
			$tmpCnt=1;  
			for($j=$i+1; $j<$other_count; $j++)  
			{  
				if($other[$j] - $other[$i] > 2){
					break;  
				} 
				$tmpCnt++;
				if($tmpCnt == 2)              //顺子
				{
					$analysis['shunAC'][]=$other[$i];
					$analysis['shunAC'][]=$other[$i+1];
					unset($other[$i]);
					unset($other[$i+1]);
					break;
				} 
			}  
			$i += $tmpCnt-1;  
		}  

		$tmp_arr = array_merge($analysis['ke'], $analysis['pair']);
		if(count($tmp_arr) > 0){
			$tmp_arr = array_unique($tmp_arr); 
			$tmp_arr = array_values($tmp_arr);
		} else {
			$tmp_arr = array_unique($analysis['shunABC']); 
			$tmp_arr = array_values($tmp_arr);
		}

		$tmp_cnt = count($tmp_arr);

		//$other = array_merge($tmp_arr, $other);
		$other = array_values($other);
		$other_count = count($other);

		for($i=0; $i<$other_count; $i++)  
		{  
			$classify_flag = false;
			for($j=0; $j<$tmp_cnt; $j++)  
			{  
				if( $tmp_arr[$j] - $other[$i]== 1 && $other[$i]%10!=1 && $tmp_arr[$j]%10!=9
					|| $other[$i] - $tmp_arr[$j] == 1 && $tmp_arr[$j]%10!=1 && $other[$i]%10!=9){

					$analysis['singleAB'][]=$other[$i];
					unset($other[$i]);
					$classify_flag = true;
					break;  
				}
			}

			if($classify_flag){
				continue;
			} 

			for($j=0; $j<$tmp_cnt; $j++)  
			{  
				if( abs($tmp_arr[$j] - $other[$i]) < 3 ){

					$analysis['singleAC'][]=$other[$i];
					unset($other[$i]);
					$classify_flag = true;
					break;  
				}
			}

			if(!$classify_flag){
				$analysis['single'][]=$other[$i];
			} 
		}
		if(count($analysis['single']) > 0){
			$this->sortSingleCard($analysis['single']);
		} else if(count($analysis['singleAC']) > 0){
			$this->sortSingleCard($analysis['singleAC']);
		} else if(count($analysis['singleAB']) > 0){
			$this->sortSingleCard($analysis['singleAB']);
		}

		return $analysis;  
	}

	private function sortSingleCard(&$cards){
		usort($cards, function ($a, $b){
			if(intval($b/10) == 18 || intval($b/10) == 19 ){
				return -1;
			} else if(intval($a/10) == 18 || intval($a/10) == 19 ){
				return 1;
			} else {
				return abs($a%10 - 5) - abs($b%10 - 5);
			}
			return  $this->_cVal($b) - $this->_cVal($a);
		});
		return $cards;
	}

	public function chooseCardToThrow($cards){

		$analysis = $this->analysisCard($cards);

		$arr =  array_merge($analysis['joker'],$analysis['ke'],$analysis['shunABC'],$analysis['pair'],$analysis['shunAB'],$analysis['shunAC'],$analysis['singleAB'],$analysis['singleAC'],$analysis['single']);
		$len = count($arr);
		echo "张数:".count($cards).PHP_EOL;
		echo "分析后的张数:".$len.PHP_EOL;
		echo "要打的牌:".$arr[$len-1].PHP_EOL;
		return $arr[$len-1];
	}



	//是否13幺胡牌
	public function check13Y_HU($card){
		$cnt = count($card);
		if($cnt != 14){
			return false;
		}
		$style = array(
			'joker' => 0,
			121 => 0,
			129 => 0,
			141 => 0,
			149 => 0,
			161 => 0,
			169 => 0,
			180 => 0,
			183 => 0,
			186 => 0,
			189 => 0,
			193 => 0,
			196 => 0,
			199 => 0,
			);
		foreach ($card as $value) {
			if($value < 100){
				$style['joker']++;
			} else if(!isset($style[$value])){
				return false;
			} else {
				$style[$value]++;
			}
		}
		$need = 0;
		foreach ($style as $key=>$value) {
			if($key != 'joker' && $value == 0){
				if(++$need > $style['joker']){
					return false;
				}
			}
		}
		echo "胡13幺".PHP_EOL;
		return true;
	}

	//是否7对
	public function check7D_HU($card){
		$cnt = count($card);
		if($cnt != 14){
			return false;
		}
		$style = array(
			'joker' => 0,
			);
		foreach ($card as $value) {
			if($value < 100){
				$style['joker']++;
			} else if(!isset($style[$value])){
				$style[$value]=1;
			} else {
				$style[$value]++;
			}
		}
		$need = 0;
		foreach ($style as $key=>$value) {
			if($key != 'joker' && $value%2 == 1){
				if(++$need > $style['joker']){
					return false;
				}
			}
		}
		echo "胡7对".PHP_EOL;
		return true;
	}

	public function check5Pai_gui($v){

		$gui_count = 0;
		for ($i=0; $i < 5; $i++) { 
			if($v[$i] < 100 ){
				$gui_count++;
			} else {
				break;
			}
		}

		if($gui_count >= 3){
			return true;
		}else if($gui_count == 2){
			if($v[3]-$v[2]<3 ){
				return true;
			} 
			if($v[4]-$v[3]<3 ){
				return true;
			} 
			return false;
			
		} else if($gui_count == 1){
			if(($v[1] == $v[2] - 1 && $v[2] == $v[3] - 1) || ($v[1] == $v[3])){
				return true;
			}
			if(($v[2] == $v[3] - 1 && $v[3] == $v[4] - 1) || ($v[2] == $v[4])){
				return true;
			}
			if( $v[4] - $v[1] < 3){
				return true;
			}
			if($v[1] == $v[2] && $v[4] - $v[3] < 3){
				return true;
			}
			if($v[3] == $v[4] && $v[2] - $v[1] < 3){
				return true;
			}
			return false;
		} else {
			if($v[0] == $v[1] && ($v[2] == $v[4] || $v[2] == $v[3] - 1 && $v[3] == $v[4] - 1)  ){
				return true;
			}
			if($v[1] == $v[3] &&   $v[0] == $v[1] - 1 && $v[3] == $v[4] - 1){
				return true;
			}
			if($v[3] == $v[4] && ($v[0] == $v[2] || $v[0] == $v[1] - 1 && $v[1] == $v[2] - 1)  ){
				return true;
			}
			return false;
		}
		return false;

	}

	private function check3Pai_gui($v){

		$this->call_count++;

		//return $this->checkAAA_gui($v) || $this->checkABC_gui($v);
		$gui_count = 0;
		//$zi_count = 0;
		for ($i=0; $i < 3; $i++) { 
			if( $v[$i] < 100 ){
				$gui_count++;
			} else {
				break;
			}
		}

		if($gui_count >= 2){
			return true;
		}else if($gui_count == 1){
			if($v[2]-$v[1]<3 ){
				return true;
			} else {
				return false;
			}
		} else {
			return ($v[0] == $v[1] - 1 && $v[1] == $v[2] - 1) || ($v[0] == $v[2]);
		}

	}

	//检测是否胡牌（张）
	private function checkAA($v){
		return $v[0] == $v[1];
	}
	//检测是否三连张
	private function checkABC($v){
		return $v[0] == $v[1] - 1 && $v[1] == $v[2] - 1;
	}
	//检测是否三重张
	private function checkAAA($v){
		return $v[0] == $v[2];
	}

	private function check3Pai($v){
		return $v[0] == $v[2] || $v[0] == $v[1] - 1 && $v[1] == $v[2] - 1;
	}

	//检测是否四重张
	private function checkAAAA($v){
		return $v[0] == $v[3];
	}


	//检测是否胡牌（张）
	private function checkAA_gui($v){

		return $v[0] == $v[1] || $v[0] < 100 || $v[1] < 100 ;
	}
	//检测是否三连张
	private function checkABC_gui($v){

		$gui_count = 0;
		for ($i=0; $i < 3; $i++) { 
			if($v[$i] < 100 ){
				$gui_count++;
			} else {
				break;
			}
		}

		if($gui_count >= 2){
			return true;
		}else if($gui_count == 1){
			if($v[2]-$v[1]<3 ){
				return true;
			} else {
				return false;
			}
		} else {
			return $v[0] == $v[1] - 1 && $v[1] == $v[2] - 1;
		}
	}
	//检测是否三重张
	private function checkAAA_gui($v){
		$gui_count = 0;
		for ($i=0; $i < 3; $i++) { 
			if($v[$i] < 100 ){
				$gui_count++;
			} 
		}
	
		if($gui_count >= 2){
			return true;
		}else if($gui_count == 1){
			return $v[1]==$v[2];
		} else {
			return $v[0] == $v[2];
		}
	}


	public $call_count = 0;

	public function isCardHu($card){

		$cnt = count($card);
		if($cnt > 5){
			for ($i=0; $i < $cnt-2; $i++) { 
				for ($j=$i+1; $j < $cnt-1; $j++) { 
					for ($k=$j+1; $k < $cnt; $k++) { 
						$tmp = $card;
						$arr1 = array($tmp[$i],$tmp[$j],$tmp[$k]);
						if($this->check3Pai_gui($arr1) ){
							unset($tmp[$i]);unset($tmp[$j]);unset($tmp[$k]);
							$arr2 = array_values($tmp);
							if($this->isCardHu($arr2)){
								return true;
							}
						}
					}
				}
			}

		} else if($cnt == 5){
			return $this->check5Pai_gui($card);
		} else if($cnt == 3){
			return $this->check3Pai_gui($card);

		} else if($cnt == 2){
			return $this->checkAA_gui($card);
                } else if($cnt == 0){
                        return true;
		} else {
			return false;
		}
		return false;
	}

	/*
		返回值  false 表示肯定不能胡牌
	*/
	public function preprocess(&$card){

		$cnt = count($card);
		if($cnt <= 3){
			return true;
		}

		$joker_array = array();
		$other = array();
		for ($i=0; $i < $cnt; $i++) { 
			if($card[$i] < 100 ){
				$joker_array[] = $card[$i];
			} else {
				$other = array_slice($card, $i);
				break;
			}
		}
		$joker_count = count($joker_array);
		$other_count = count($other);
		$single_count = 0;
		

		$oth = $other;
		for ($i=0; $i < $other_count; $i++) { 
			if( ($i-1<0 || $oth[$i]-$oth[$i-1]>2) && ($i+1>=$other_count || $oth[$i+1]-$oth[$i]>2) ){
				$single_count++;
				unset($other[$i]);
			}
		}

		if($single_count > 0){
			$need_joker_cnt = $single_count * 2 - 1;
			if($joker_count < $need_joker_cnt){
				$card = array();
				return false;
			} else {
				for ($i=0; $i < $need_joker_cnt; $i++) { 
					unset($joker_array[$i]);
				}
			}
		}

		$card = array_merge($joker_array, $other);
		sort($card);
		return true;
	}

	protected function getMillisecond() {
		list($t1, $t2) = explode(' ', microtime());
		//$t2 = substr($t2, -2);
		return floatval($t1)+floatval($t2);
	}

	public function checkCardHu($card){
		$this->call_count = 0;
		$begin_time = $this->getMillisecond();

		//十三幺
		if($this->check13Y_HU($card)){
			return 3; 
		}
		//七对
		if($this->check7D_HU($card)){
			return 2; 
		}

		//$jiang_count = 0;

		$result = $this->preprocess($card);
		if($result==false){	//不能胡牌，直接返回
			return 0;
		}

		$result = $this->isCardHu($card);

		$end_time = $this->getMillisecond();
		$spend = $end_time - $begin_time;

		//echo  "耗时:". sprintf('%.4f',$spend) ."     递归:".$this->call_count.PHP_EOL;

		if($this->call_count > 10000 || $spend > 0.01){
			$card_str = "  [". implode(",", $card) ."]";
			$content = "耗时:". sprintf('%.4f',$spend) ." 递归:".$this->call_count . $card_str."\n";
			$file = dirname(__DIR__).'/Logs/hu_logic_card.log';
			file_put_contents($file, $content,FILE_APPEND);
		}

		return $result;
	}

	public static function instance()
	{   
	    if(!self::$instance)
	    {
	        self::$instance = new Mahjong();
	    }
	    return self::$instance;
	}
}
